/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: Alarm_Dummy.cpp,v 1.10 2009-05-13 07:18:20 amaula Exp $
 *
 * \file
 * \brief Example module for the Alarm interface.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 ******************************************************************************
 * Module Info:
 *
 * Provides Interfaces:
 * - Alarm
 *
 * Needs Interfaces:
 *
 * General information:
 * - Just dummy interface for testing the Alarm interface.
 * 
 ******************************************************************************
 */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "owndebug.h"
#include "ownutils.h"
#include "binbag.h"
#include "AlarmServer.hpp"
#include <string>

//*****************************************************************************
// Include GIMI in proper way
#define GIMI_CLIENT_API (20000)
#include "gimi.h"
//*****************************************************************************
//*****************************************************************************

enum EAlarmDummyMode { 
  KAlarmDummyModeFortune,
  KAlarmDummyModeStrings,
  KAlarmDummyModeRandomPrintableChars,
  KAlarmDummyModeOnlyQuestions 
};
//*****************************************************************************
//*****************************************************************************
static int verbose = 1;
static EAlarmDummyMode mode = KAlarmDummyModeFortune;
static unsigned int alarmEventInterval = 1000;
//*****************************************************************************
// Choose your namespace
using namespace MaCI;
using namespace MaCI::Alarm;
//*****************************************************************************
// Running?
static volatile bool run = true;
static ownThreadHandle signalHandlerThread;
//*****************************************************************************
// Own.
static std::string macigroup = "Dummy";
static std::string groupinstance = "Dummy";
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

static int SignalHandlerThread(void *)
{
  dPrint(8, "Signal handler thread (%ld) starting...", pthread_self());

  while (run) {
    // Now, wait for requested signals to arrive
    int sig;
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGPIPE);
    sigaddset(&mask, SIGHUP);
    sigwait(&mask, &sig);

    // Got signal, sigwait returned!
    dPrint(3,"Got Signal '%d' (%s)", sig, strsignal(sig));

    // What to do?
    switch(sig) {
    case SIGINT:
    case SIGTERM:
    case SIGPIPE:
    case SIGHUP:
      dPrint(1,"Got terminating signal. Please wait. (%d: '%s')", sig, strsignal(sig));
      run = false;
      ownSleep_ms(1000);
      break;
      
    default:
      dPrint(3,"Unhandled signal! Ignore!");
      break;
    }
  }
  return 0;
}
//*****************************************************************************

static void InitSignalHandler(void)
{
  // Now; Set to BLOCK ALL SIGNALS
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGINT);
  sigaddset(&mask, SIGTERM);
  sigaddset(&mask, SIGPIPE);
  sigaddset(&mask, SIGHUP);
  pthread_sigmask(SIG_BLOCK, &mask, NULL);
  
  // Start signal handler thread
  signalHandlerThread = ownThread_Create((void*)SignalHandlerThread, NULL);
}
//*****************************************************************************
//*****************************************************************************

/**
 * All MaCI modules should support these parameters with exactly these names
 * *** DO NOT CHANGE NOR DELETE ANY OF THEM ***
 * Howevery, feel free to add new parameters as you wish.
 */
static void showUsage()
{
  fprintf(stderr,"\n");
  fprintf(stderr,"Usage:\tAlarm_Dummy [OPTIONS]\n");
  fprintf(stderr,"  [-u addr]\t\t\tUse 'addr' as GIMnet AccessPoints address\n");
  fprintf(stderr,"  [-p port]\t\t\tUse 'port' as GIMnet AccessPoints port\n");
  fprintf(stderr,"  [-n name]\t\t\tUse 'name' as this nodes GIMnet name\n");
  fprintf(stderr,"  [-G name]\t\t\tSpecify the MaCIGroup for this module\n");
  fprintf(stderr,"  [-I name]\t\t\tSpecify the MaCIGroup Interface Instance\n");
  fprintf(stderr,"  [-m mode]\t\t\tSelect mode; 'fortune', 'strings', 'random' or 'questions'\n");
  fprintf(stderr,"  [-i interval]\t\t\tInterval between sent events\n");
  
  fprintf(stderr,"  [-v]\t\t\t\tIncrease verbosity by 1\n");
  fprintf(stderr,"  [-q]\t\t\t\tQuiet mode\n");
  fprintf(stderr, "\n\n");
}
//*****************************************************************************

static int alarmWatch(CAlarmServer *as)
{
  CAlarmData ad;

  EMaCIError e;
  while(run) {
    e = as->WaitCommand(ad, 1000);
    if (e == KMaCIOK) {
      const TCommand *c = ad.GetCommand();
      if (c) {

        dPrint(1,"Got command: %d, data %d", c->cmd, c->data);
      } else {
        dPrint(1,"Got Command, but no Command element present???");

      }
    }
  }
  
  return 0;
}
//*****************************************************************************

static int ExecuteModule(gimi::GIMI &g)
{
  MaCICtrl::CMaCICtrlServer mcs(&g, 0);
  CAlarmServer as(&mcs, 0, "(RGB) DummyAlarms", "Alarm_Dummy");
  CAlarmData ad;
  ownThreadHandle alarmWatchThread;
  unsigned int alarmSeqId = 0;
  const char textTable[10][100] = {
    "Hello world!",
    "How's going?",
    "This is an event. Got it?",
    "I'm so tired",
    "I'm so Bored!",
    "Lets do some EVENT business!",
    "Events! Who could live without them?",
    "This event will crash your software!",
    "Windows Server 2008 warming up...",
    "That's is." 
  };


  srandom(time(NULL));

  // Initialize AlarmData to use infernal binbag.
  ad.CreateInternalBinBag();

  // Set GroupName for MaCICtrlServer and Instance for AlarmServer.
  mcs.SetGroupName(macigroup);
  as.SetInterfaceInstanceName(groupinstance);
  
  // Open.
  if (as.Open() == KMaCIOK) {

    // Run!
    alarmWatchThread = ownThread_Create((void*)alarmWatch, &as);

    // Trigger some events.
    while(run) {
      // Construct.
      ad.Reset();
      ad.SetSourceNameText(macigroup+".MaCI_Alarm."+groupinstance);
      ad.SetTimestamp(Common::TTimestamp());
      ad.SetAlarmEvent(TAlarmEvent(2 + (random() % 6), 
                                   (random() % 30),
                                   0,
                                   ++alarmSeqId));

      if (mode == KAlarmDummyModeFortune) {
        // Fortune mode.

        char foo[4096];
        FILE *fo = popen("fortune","r");
        if (fo) {
          foo[fread(foo, 1, sizeof(foo), fo)-1] = '\0';
          pclose(fo);
          ad.SetHeadingText(std::string(foo).substr(0, 64) + std::string("..."));
          ad.SetText(foo);
          
        } else {
          ad.SetHeadingText("Pseudo-Random clause");
          ad.SetText(textTable[random() % 10]);
        }
        
      } else if (mode == KAlarmDummyModeStrings) {
        // Pseudorandom line.
        
        ad.SetHeadingText("Pseudo-Random clause");
        ad.SetText(textTable[random() % 10]);

      } else if (mode == KAlarmDummyModeRandomPrintableChars) {
        // Random chars mode.
        char bigbuf[4096];
        const int nchars = random() % (sizeof(bigbuf)-1);
        for (int i=0; i < nchars; ++i) {
          bigbuf[i] = 32 + (random() % (126-32));
        }
        bigbuf[nchars] = '\0';
        std::string foo;
        ad.SetHeadingText("Totally random s***");
        ad.SetText(bigbuf);

      } else if (mode == KAlarmDummyModeOnlyQuestions) {
        // Reset here, because this overrides the predefined values.
        ad.Reset();
        ad.SetSourceNameText(macigroup+".MaCI_Alarm."+groupinstance);
        ad.SetTimestamp(Common::TTimestamp());
        ad.SetAlarmEvent(TAlarmEvent(KEventTypeQuestion,
                                     30,
                                     0,
                                     ++alarmSeqId));
        char n[64];
        sprintf(n,"%u", alarmSeqId);
        ad.SetHeadingText("Question no. " + std::string(n));
        ad.SetText("Question no. " + std::string(n) + " needs an answer!");
        
      } else {
        dPrint(ODERROR,"Invalid mode: %d", mode);
        abort();
      }

      // Print.
      dPrint(1,"Sending:");
      ad.Print(1);
      
      // Send!
      as.SendAlarmEvent(ad);

      
      // Wait for a moment.
      //      const unsigned int sleepTime = 1000 + random() % 5000;
      //      const unsigned int sleepTime = 1 + random() % 500;
      dPrint(1,"Sleeping for %u milliseconds...", alarmEventInterval);
      ownSleep_ms(alarmEventInterval);
      
    }

  } else {
    dPrint(1,"Failed to Open() AlarmServer Interface! Fatal error! Exit!");

  }
   
  return 0;
}
//*****************************************************************************

int main(int argc, char *argv[]) 
{
  // GIMnet parameters
  std::string gimnetAP = "asrobo.hut.fi";
  int gimnetAPPort = 50002;
  std::string gimnetName;

  // Configuration parameters
  std::string configurationFile;
  std::string configurationBasekey;

  // setup signal handling
  InitSignalHandler();

  // Objects
  gimi::GIMI g;

  // Init Debug lib
  debugInit();
  debugSetGlobalDebugLvl(1);
  const std::string logfile = std::string(argv[0]) + ".log";
  debugSetLogFilename(logfile.c_str());

  // Greet
  fprintf(stderr,
	  "\n%s - $Revision: 1.10 $\n", argv[0]);

  /////////////////////////////////////////////////////////////////////////////
  // Parse parameters - Remember: ** DO NOT MODIFY OR DELETE ANY OF THIS **
  // _Add_ anything as you wish.
  ///////////////////////////////////////////////////////////////////////////// 
  bool parse = true;
  while(parse) {
    int c;
    c = getopt(argc, argv, "i:m:n:u:p:G:I:hqv");
    switch(c) {

      /////////////////////////////////
      ////// Application specific /////
      /////////////////////////////////

      /////////////////////////////////////////////////////////////////////////
      /// Add here anything you need.
      /////////////////////////////////////////////////////////////////////////
    case 'm': {
      const std::string optarg_str = optarg;
      if (optarg_str == "fortune") {
        mode = KAlarmDummyModeFortune;
      } else if (optarg_str == "strings") {
        mode = KAlarmDummyModeStrings;
      } else if (optarg_str == "random") {
        mode = KAlarmDummyModeRandomPrintableChars;
      } else if (optarg_str == "questions") {
        mode = KAlarmDummyModeOnlyQuestions;
      } else {
        dPrint(1,"Invalid mode: '%s', please specify one of: 'fortune', 'strings', 'random' or 'questions'",
               optarg);
        exit(1);
      }
      break;
    }

      /////////////////////////////////
      ///// MaCI/GIMnet parameters ////
      /////////////////////////////////
    case 'n':
      gimnetName = optarg;
      dPrint(3,"Using GIMnet Name '%s'", optarg);
      break;

    case 'u':
      gimnetAP = optarg;
      dPrint(3,"Using GIMnetAP address '%s'", optarg);
      break;

    case 'p':
      gimnetAPPort = atoi(optarg);
      dPrint(3,"Using GIMnetAP port '%d'", gimnetAPPort);
      break;

    case 'G':
      macigroup = optarg;
      dPrint(3,"Using MaCI-Group '%s'", optarg);
      break;

    case 'I':
      groupinstance = optarg;
      dPrint(3,"Using MaCI-Group Interface Instance '%s'", optarg);
      break;

    case 'i':
      alarmEventInterval = atoi(optarg);
      dPrint(3,"Using event Interval %ums", 
             alarmEventInterval);
      break;
      //////////////////////////////////
      //// GENERIC:                 ////
      //////////////////////////////////
    case 'v':
      // Increase only if not previously set to 0 (by 'quiet')
      if (verbose > 0)
	debugSetGlobalDebugLvl(++verbose);
      break;
      
    case 'q':
      verbose = 0;
      debugSetGlobalDebugLvl(0);
      break;
      
    case 'h':
    case '?':
      showUsage();
      exit(1);
      break;
      
    case -1:
      parse = false;
      break;
    }
  }
  
  /////////////////////////////////////////////////////////////////////////////
  // Execute connection to GIMnet.
  /////////////////////////////////////////////////////////////////////////////
  int r;
  if ( (r = g.connectToHubEx(gimnetAP, gimnetAPPort, gimnetName) ) != GIMI_OK) {
    dPrint(1,"Failed to connect to GIMnet AccessPoint '%s:%d' with name '%s': '%s'",
	   gimnetAP.c_str(), 
	   gimnetAPPort, 
	   gimnetName.size()?gimnetName.c_str():"<anonymous>",
	   gimi::getGimiErrorString(r).c_str());
    
  } else {
    dPrint(2,"Succesfully connected to GIMnet at AccessPoint '%s:%d' with name '%s': '%s'",
	   gimnetAP.c_str(), 
	   gimnetAPPort, 
	   gimnetName.size()?gimnetName.c_str():"<anonymous>",
	   gimi::getGimiErrorString(r).c_str());
    
    // GIMnet connection OK, now execute module.
    r = ExecuteModule(g);
  }

  return 0;
}
//*****************************************************************************
//*****************************************************************************
/******************************************************************************
 * 
 * $Log: not supported by cvs2svn $
 * Revision 1.9  2009-04-27 08:05:03  amaula
 * Added some features to ease testing against the module
 *
 * Revision 1.8  2009-03-17 08:46:52  amaula
 * Heading line is now little longer.
 *
 * Revision 1.7  2009-03-17 08:39:21  amaula
 * Some little modification to ease up testing.
 *
 * Revision 1.6  2009-03-11 09:23:34  amaula
 * Added sourceName call.
 *
 * Revision 1.5  2009-03-06 08:21:58  amaula
 * Added Heading.
 *
 * Revision 1.4  2009-01-12 13:46:45  amaula
 * Merged in branch: MaCI-interface-Alarm-callback-implementation-branch
 *
 * Revision 1.3.2.1  2009-01-09 13:09:43  amaula
 * Added implementation for Alarm callback.
 *
 * Revision 1.3  2008-11-10 12:43:17  amaula
 * Added module info fields.
 *
 * Revision 1.2  2008-11-10 08:16:58  amaula
 * interfaces/
 *
 * Revision 1.1  2008-11-07 11:12:46  amaula
 * Lots of misc. changes.
 *
 *
 *
 *****************************************************************************/
//*****************************************************************************
//*****************************************************************************


